package com.shine.cotrun.exp;

import java.io.BufferedOutputStream;
import java.io.IOException;
import java.lang.reflect.Method;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Collection;
import java.util.Date;

import com.shine.cotrun.exp.ExportConf.ExportField;



public class ExportToDBF<D> extends Export<D> {
	
	private BufferedOutputStream bos;
	private String encoding;
	private int recCount;//总记录数
	private DBFField[] fields;
	private int recordLength;//所有字段长度相加
	
	public ExportToDBF(ExportConf conf) {
		super(conf);
		this.extName = ".dbf";
		this.encoding = "gbk";
		fields = new DBFField[conf.getColumnSize()];
		for (int i = 0 ; i < conf.getColumnSize(); i++) {
			ExportField f = conf.getField(i);
			DBFField df = new DBFField();
			df.name = f.getExpColumnName();
			Class<?> c = f.getType();
			if (Date.class.isAssignableFrom(c)) {
				df.type = 'D';
				df.length = 8;
			} else if (Boolean.class.isAssignableFrom(c)) {
				df.type = 'L';
				df.length = 1;
			} else if (Long.class.isAssignableFrom(c) || Integer.class.isAssignableFrom(c)
					|| Short.class.isAssignableFrom(c) || Byte.class.isAssignableFrom(c)) {
				df.type = 'N';
				if (f.getLength() > 0 && f.getLength() <= 21) {
					df.length = f.getLength();
				} else {
					df.length = 21;
				}
			} else if (Number.class.isAssignableFrom(c)) {
				df.type = 'F';
				if (f.getLength() > 0 && f.getLength() <= 21) {
					df.length = f.getLength();
				} else {
					df.length = 21;
				}
				df.decimalCount = 2;//默认2位小数点
			} else {
				df.type = 'C';
				if (f.getLength() > 0 && f.getLength() <= 254) {
					df.length = f.getLength();
				} else {
					df.length = 254;//长度都取最大
				}

			}
			fields[i] = df;
			recordLength += df.length;
		}
		
	}
	
	public void setEncoding(String encoding) {
		this.encoding = encoding;
	}
	
	@Override
	public void init() {
		super.init();
		try {
			bos = new BufferedOutputStream(this.outputStream);
			//写入头部信息
			writeHeader();
			for (int i = 0; i < conf.getColumnSize(); i++) {
				writeFieldHeader(fields[i]);
			}
			bos.write(13);
			bos.flush();
			
		} catch(Exception e) {
			throw new RuntimeException(e);
		}
	}
	
	/**
	 * 写入文件头部
	 * @throws IOException
	 */
	private void writeHeader() throws IOException {
		byte abyte0[] = new byte[16];
		abyte0[0] = 3;
		Calendar calendar = Calendar.getInstance();
		abyte0[1] = (byte) (calendar.get(Calendar.YEAR) - 1900);
		abyte0[2] = (byte) calendar.get(Calendar.MONTH);
		abyte0[3] = (byte) calendar.get(Calendar.DAY_OF_MONTH);
		abyte0[4] = 0;
		abyte0[5] = 0;
		abyte0[6] = 0;
		abyte0[7] = 0;
		int i = (conf.getColumnSize() + 1) * 32 + 1;
		abyte0[8] = (byte) (i % 256);
		abyte0[9] = (byte) (i / 256);
		int j = 1;
		for (int k = 0; k < conf.getColumnSize(); k++) {
			j += fields[k].length;
		}
		abyte0[10] = (byte) (j % 256);
		abyte0[11] = (byte) (j / 256);
		abyte0[12] = 0;
		abyte0[13] = 0;
		abyte0[14] = 0;
		abyte0[15] = 0;
		bos.write(abyte0, 0, abyte0.length);
		for (int l = 0; l < 16; l++) {
			abyte0[l] = 0;
		}
		bos.write(abyte0, 0, abyte0.length);
	}

	/**
	 * 写入字段头部
	 * @param field
	 * @throws IOException
	 */
	private void writeFieldHeader(DBFField field) throws IOException {
		//截取名称，字节长度（中文点2字节）不能超过10
		String name = field.name;
		StringBuffer newName = new StringBuffer();
		int nameCount = 0;
		/* 循环从新组成字符串，此字符串的字节长度不能大于10 */
		for (int i = 0; i < name.length(); i++) {
			int length = name.substring(i, i + 1).getBytes().length;
			if ((length + nameCount) > 10) { /* 字节长度大于1的时候为汉字 */
				break;
			} else {
				nameCount = nameCount + length;
				newName.append(name.charAt(i));
			}
		}

		/* 定义一个新数组，用来接收新构造的字符串字节数组 */
		byte[] nbyte = newName.toString().getBytes();
		byte abyte0[] = new byte[16];
		/* 将字数组数据合并到文件头数据组 */
		for (int i = 0; i < nbyte.length; i++) {
			abyte0[i] = nbyte[i];
		}
		/* 在没有地方补空 */
		for (int i = nameCount; i <= 10; i++) {
			abyte0[i] = 0;
		}
		abyte0[11] = (byte)field.type;
		abyte0[12] = 0;
		abyte0[13] = 0;
		abyte0[14] = 0;
		abyte0[15] = 0;

		bos.write(abyte0, 0, abyte0.length);

		for (int l = 0; l < 16; l++)
			abyte0[l] = 0;

		abyte0[0] = (byte) field.length;
		abyte0[1] = (byte) field.decimalCount;
		bos.write(abyte0, 0, abyte0.length);
	}
	
	@Override
	public void export(Collection<D> data) throws Exception {
		for (Object o : data) {
			Class<?> c = o.getClass();
			
			byte abyte0[] = new byte[recordLength];
			int k = 0;
			for (int i = 0; i < conf.getColumnSize(); i++) {
				Method m = c.getMethod(conf.toMethod(conf.getField(i).getExpPropertie()));
				Object val = m.invoke(o);//字段对应值
				String s = formatValue(fields[i], val);
				byte abyte1[];
				if (encoding != null) {
					abyte1 = s.getBytes(encoding);
				} else {
					abyte1 = s.getBytes();
				}
				for (int i1 = 0; i1 < fields[i].length; i1++) {
					abyte0[k + i1] = abyte1[i1];
				}
				k += fields[i].length;
			}
	
			bos.write(32);
			bos.write(abyte0, 0, abyte0.length);
		}
		bos.flush();
		recCount += data.size();
		
	}
	
	/**
	 * 格式化字段的值
	 * @param field
	 * @param obj
	 * @return
	 * @throws Exception
	 */
	private String formatValue(DBFField field, Object obj) throws Exception {
		if (field.type == 'C' || field.type == 'N' || field.type == 'F') {
			String s = (obj == null) ? "" : obj.toString();
			if (s.length() == field.length) {
				return s;
			} else if (s.length() > field.length) {//截超长部分
				return s.substring(0, field.length);
			} else {
				StringBuffer sb = new StringBuffer(field.length - s.length());
				for (int i = 0; i < field.length - s.length(); i++)
					sb.append(' ');
	
				return s + sb;
			}
		}
		if (field.type == 'L') {
			if (obj == null) {
				obj = new Boolean(false);
			}
			if (obj instanceof Boolean) {
				Boolean b = (Boolean)obj;
				return b.booleanValue() ? "Y" : "N";
			} else {
				return "N";//非逻辑类型返回N
			}
		}
		if (field.type == 'D') {
			if (obj == null) {
				obj = new Date();
			}
			if (obj instanceof Date) {
				Date date = (Date)obj;
				SimpleDateFormat sf = new SimpleDateFormat("yyyyMMdd");
				return sf.format(date);
			} else {
				return "19000101";
			}
		} else {
			throw new Exception("未知类型: " + field.type);
		}
	}

	@Override
	public String end() {
		try {
			bos.close();
		} catch (Exception e) {
			throw new RuntimeException(e);
		}
		return super.end();
	}
	
	static class DBFField {
		private String name;
		private char type;
		private int length;
		private int decimalCount;
	}
}
